package com.xyy.saas.payment.api.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.util.concurrent.RateLimiter;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.RocketMqTopic;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.config.BusinessTypeConfig;
import com.xyy.saas.payment.cores.api.PaymentOrderApi;
import com.xyy.saas.payment.cores.bo.*;
import com.xyy.saas.payment.cores.constants.PayExtraConstants;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.param.*;
import com.xyy.saas.payment.cores.vo.*;
import com.xyy.saas.payment.dao.mapper.WithdrawOrderMapper;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.merchant.core.dto.QueryTradeCertificateDto;
import com.xyy.saas.payment.merchant.core.dto.TradeCertificateVo;
import com.xyy.saas.payment.merchant.core.service.SeparateOrderService2;
import com.xyy.saas.payment.payment.core.dto.*;
import com.xyy.saas.payment.payment.core.service.PaymentService;
import com.xyy.saas.payment.payment.core.service.RefundOrderService;
import com.xyy.saas.payment.query.core.service.QueryTradeService;
import com.xyy.saas.payment.query.core.trade.TransactionQuery;
import com.xyy.saas.payment.query.core.trade.TransactionQueryFactory;
import com.xyy.saas.payment.service.PayResultOrderService;
import com.xyy.saas.payment.service.SeparateSummaryService;
import com.xyy.saas.payment.util.*;
import com.xyy.saas.payment.util.mq.MqProducer;
import com.xyy.util.StringUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.util.CollectionUtils;
import util.LukeRsaUtil;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author wangtianqi
 * @date 2019/11/22 17:25
 */
@DependsOn({"apolloUtil","leafUtil"})
@Service(version = "1.0.0", loadbalance = "roundrobin")
public class PaymentOrderApiImpl implements PaymentOrderApi, InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(PaymentOrderApiImpl.class);
    @Resource
    private CallbackRunner callbackRunner;
    @Autowired
    private PaymentService paymentService;

    @Autowired
    private PayResultOrderService payResultOrderService;

    @Autowired
    private RefundOrderService refundOrderService;

    @Autowired
    private QueryTradeService queryTradeService;

    @Autowired
    private LeafUtil leafUtil;

    @Autowired
    private WithdrawOrderMapper withdrawOrderMapper;

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private SeparateSummaryService separateSummaryService;

    @Autowired
    private SeparateOrderService2 separateOrderService2;

    @Autowired
    private BusinessTypeConfig businessTypeConfig;

    @Autowired
    private MqProducer mqProducer;

    private int rateLimiterCreate = Integer.parseInt(ApolloUtil.getProperty(Constants.PAY_RATE_LIMITER_CREATE));

    private RateLimiter rateLimiter = RateLimiter.create(rateLimiterCreate);

    @Override
    public ResultVO<NotifyOfflineRefundBo> notifyOfflineRefund(NotifyOfflineRefundParam notifyOfflineRefundParam) {
        try {
            if (notifyOfflineRefundParam == null || StringUtils.isBlank(notifyOfflineRefundParam.getRefundNo()) ||
                    StringUtils.isBlank(notifyOfflineRefundParam.getBusinessRefundNo()) ||
                    notifyOfflineRefundParam.getAmount() == null || notifyOfflineRefundParam.getRefundType() == null) {
                return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
            }

            return paymentService.notifyOfflineRefund(notifyOfflineRefundParam);
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#notifyOfflineRefund error, notifyOfflineRefundParam:{}", JSON.toJSONString(notifyOfflineRefundParam), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO prepay4cashier(OrderPrepayVo orderPrepayVo, String sign) {
        try {
            // 参数校验
            paymentService.checkOrderPrepayVo(orderPrepayVo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#prepay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(orderPrepayVo), e.getErrCode());
            return ResultVO.createParamError();
        }

        try {
            String publicKey = ApolloUtil.getProperty(orderPrepayVo.getInvoker().getType() + Constants.PUBLIC);
            if (!SignUtil.doCheck(orderPrepayVo, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#prepay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(orderPrepayVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            PrepayDto prepayDto = new PrepayDto();
            prepayDto.setBusinessId(orderPrepayVo.getPayeeId());
            prepayDto.setBusinessOrderNo(orderPrepayVo.getBusinessOrderNo());
            prepayDto.setAmount(orderPrepayVo.getAmount());
            prepayDto.setBusinessType(orderPrepayVo.getBusinessType().getType());
            prepayDto.setBusinessIdType(orderPrepayVo.getBusinessIdType().getType());
            prepayDto.setOrderType(orderPrepayVo.getBusinessOrderType().getType());
            prepayDto.setCallbackUrl(orderPrepayVo.getCallback());
            prepayDto.setPayerId(orderPrepayVo.getPayerId());
            prepayDto.setPayerName(orderPrepayVo.getPayerName());
            prepayDto.setSellerName(orderPrepayVo.getSellerName());

            return paymentService.prepay(prepayDto);
        } catch (PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#prepay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(orderPrepayVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#prepay error orderPrepayVo:{}", JSON.toJSONString(orderPrepayVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<PrepayBo> prepay4cashier(OrderPrepayVoV1 orderPrepayVoV1, String sign) {
        try {
            // 参数校验
            paymentService.checkOrderPrepayVo(orderPrepayVoV1);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#prepay warn orderPrepayVoV1:{} paymentException:{}", JSON.toJSONString(orderPrepayVoV1), e.getErrCode());
            return ResultVO.createParamError();
        }
        try {
            String publicKey = ApolloUtil.getProperty(orderPrepayVoV1.getInvoker() + Constants.PUBLIC);
            if (!SignUtil.doCheck(orderPrepayVoV1, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#prepay warn orderPrepayVoV1:{} paymentException:{}", JSON.toJSONString(orderPrepayVoV1), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            PrepayDto prepayDto = new PrepayDto();
            prepayDto.setBusinessId(orderPrepayVoV1.getPayeeId());
            prepayDto.setBusinessOrderNo(orderPrepayVoV1.getBusinessOrderNo());
            prepayDto.setAmount(orderPrepayVoV1.getAmount());
            prepayDto.setBusinessType(orderPrepayVoV1.getBusinessType());
            prepayDto.setBusinessIdType(orderPrepayVoV1.getBusinessIdType());
            prepayDto.setOrderType(orderPrepayVoV1.getBusinessOrderType());
            prepayDto.setCallbackUrl(orderPrepayVoV1.getCallback());
            prepayDto.setPayerId(orderPrepayVoV1.getPayerId());
            prepayDto.setPayerName(orderPrepayVoV1.getPayerName());
            prepayDto.setSellerName(orderPrepayVoV1.getSellerName());

            return paymentService.prepay(prepayDto);
        } catch (PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#prepay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(orderPrepayVoV1), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#prepay error orderPrepayVo:{}", JSON.toJSONString(orderPrepayVoV1), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO refund(OrderRefundVo orderRefundVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#refund begin orderRefundVo:{}", JSON.toJSONString(orderRefundVo));
        try {
            // 参数校验
            paymentService.checkOrderRefundVo(orderRefundVo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#refund warn orderRefundVo:{} paymentException:{}", JSON.toJSONString(orderRefundVo), e.getErrCode());
            return ResultVO.createParamError();
        }
        try {
            Map<String, Object> map = new HashMap<>();
            map.put("tradeNo", orderRefundVo.getTradeNo());
            map.put("payChannel", orderRefundVo.getPayChannel());
            map.put("description", orderRefundVo.getDescription());
            map.put("amount", orderRefundVo.getAmount());
            map.put("invoker", orderRefundVo.getInvoker());
            map.put("businessType", orderRefundVo.getBusinessType());
            map.put("businessRefundNo", orderRefundVo.getBusinessRefundNo());
            map.put("callback", orderRefundVo.getCallback());

            String publicKey = ApolloUtil.getProperty(orderRefundVo.getInvoker().getType() + Constants.PUBLIC);
            if (!SignUtil.doCheck(map, sign, publicKey)) {
                return ResultVO.createSignError();
            }
            RefundDto refundDto = new RefundDto();
            refundDto.setPayNo(orderRefundVo.getTradeNo());
            refundDto.setAmount(orderRefundVo.getAmount());
            refundDto.setDescription(orderRefundVo.getDescription());
            refundDto.setCallbackUrl(orderRefundVo.getCallback());
            refundDto.setBusinessType(orderRefundVo.getBusinessType().getType());
            refundDto.setBusinessRefundNo(orderRefundVo.getBusinessRefundNo());
            refundDto.setChannel(orderRefundVo.getPayChannel().getCode());
            RefundBo refundBo = paymentService.refund(refundDto);
            LOGGER.info("PaymentOrderApiImpl#refund end orderRefundVo:{} refundBo:{}", JSON.toJSONString(orderRefundVo), JSON.toJSONString(refundBo));
            return ResultVO.createSuccess(refundBo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#refund warn orderRefundVo:{} paymentException:{}", JSON.toJSONString(orderRefundVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#refund error orderRefundVo:{}", JSON.toJSONString(orderRefundVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<RefundBo> refund(OrderRefundVoV1 orderRefundVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#refund begin orderRefundVo:{}", JSON.toJSONString(orderRefundVo));

        try {
            // 参数校验
            paymentService.checkOrderRefundVo(orderRefundVo);
        }
        catch(PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#refund warn orderRefundVo:{} paymentException:{}", JSON.toJSONString(orderRefundVo), e.getErrCode());
            return ResultVO.createParamError();
        }

        try {
            String publicKey = ApolloUtil.getProperty(orderRefundVo.getInvoker() + Constants.PUBLIC);

            if(!SignUtil.doCheck(orderRefundVo, sign, publicKey)) {
                return ResultVO.createSignError();
            }

            RefundDto refundDto = new RefundDto();
            refundDto.setPayNo(orderRefundVo.getTradeNo());
            refundDto.setAmount(orderRefundVo.getAmount());
            refundDto.setDescription(orderRefundVo.getDescription());
            refundDto.setCallbackUrl(orderRefundVo.getCallback());
            refundDto.setBusinessType(orderRefundVo.getBusinessType());
            refundDto.setBusinessRefundNo(orderRefundVo.getBusinessRefundNo());
            refundDto.setChannel(orderRefundVo.getPayChannel().getCode());
            refundDto.setCommission(orderRefundVo.getRefundCommission());
            RefundBo refundBo = paymentService.refund(refundDto);
            LOGGER.info("PaymentOrderApiImpl#refund end orderRefundVo:{} refundBo:{}", JSON.toJSONString(orderRefundVo), JSON.toJSONString(refundBo));
            return ResultVO.createSuccess(refundBo);
        }
        catch(PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#refund warn orderRefundVo:{} paymentException:{}", JSON.toJSONString(orderRefundVo), e.getErrCode());
            return ResultVO.createError(e.getErrCode(), e.getErrMsg());
        }
        catch(Exception e) {
            LOGGER.error("PaymentOrderApiImpl#refund error orderRefundVo:{}", JSON.toJSONString(orderRefundVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<QueryResultBo> queryPay(OrderQueryVo orderQueryVo, String sign) {
        try {
            if (orderQueryVo==null||orderQueryVo.getBusinessOrderType()==null||orderQueryVo.getChannel()==null||orderQueryVo.getInvoker()==null||
                    orderQueryVo.getPayMode()==null||orderQueryVo.getTradeNo()==null||orderQueryVo.getTradeNo().length()>64){
                LOGGER.warn("PaymentOrderApiImpl queryPay param error orderQueryVo:{}, sign:{}", JSON.toJSON(orderQueryVo), sign);
                return ResultVO.createParamError();
            }
            Map<String, Object> map = new HashMap<>();
            map.put("tradeNo", orderQueryVo.getTradeNo());
            map.put("businessOrderType", orderQueryVo.getBusinessOrderType());
            map.put("channel", orderQueryVo.getChannel());
            map.put("invoker", orderQueryVo.getInvoker());
            map.put("payMode", orderQueryVo.getPayMode());
            String publicKey = ApolloUtil.getProperty(orderQueryVo.getInvoker().getType()+".public_key");
            if (!SignUtil.doCheck(map, sign, publicKey)){
                LOGGER.error("PaymentOrderApiImpl queryPay check sign error orderQueryVo:{}", JSON.toJSON(orderQueryVo));
                return ResultVO.createSignError();
            }
            return queryTradeService.checkPay(orderQueryVo.getTradeNo(), orderQueryVo.getChannel().getCode(), orderQueryVo.getBusinessOrderType().getType(), orderQueryVo.getPayMode().getMode());
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#checkPay warn orderCheckVo:{} paymentException:{}", JSON.toJSONString(orderQueryVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#checkPay error orderCheckVo:{}", JSON.toJSONString(orderQueryVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<QueryResultBo> queryPay(OrderQueryVoV1 orderQueryVo, String sign) {
        try {
            if (orderQueryVo==null||orderQueryVo.getBusinessOrderType()==null||orderQueryVo.getChannel()==null||orderQueryVo.getInvoker()==null||
                    orderQueryVo.getPayMode()==null||orderQueryVo.getTradeNo()==null||orderQueryVo.getTradeNo().length()>64){
                LOGGER.warn("PaymentOrderApiImpl queryPay param error orderQueryVo:{}, sign:{}", JSON.toJSON(orderQueryVo), sign);
                return ResultVO.createParamError();
            }
            String publicKey = ApolloUtil.getProperty(orderQueryVo.getInvoker()+".public_key");
            if (!SignUtil.doCheck(orderQueryVo, sign, publicKey)){
                LOGGER.error("PaymentOrderApiImpl queryPay check sign error orderQueryVo:{}", JSON.toJSON(orderQueryVo));
                return ResultVO.createSignError();
            }
            return queryTradeService.checkPay(orderQueryVo.getTradeNo(), orderQueryVo.getChannel().getCode(), orderQueryVo.getBusinessOrderType(), orderQueryVo.getPayMode().getMode());
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#checkPay warn orderCheckVo:{} paymentException:{}", JSON.toJSONString(orderQueryVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#checkPay error orderCheckVo:{}", JSON.toJSONString(orderQueryVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }
    @Override
    public ResultVO<QueryResultBo> queryPayV2(OrderQueryVoV1 orderQueryVo) {
        try {
            if (orderQueryVo==null||StringUtils.isEmpty(orderQueryVo.getTradeNo())){
                LOGGER.warn("PaymentOrderApiImpl queryPayV2 param error orderQueryVo:{}", JSON.toJSON(orderQueryVo));
                return ResultVO.createParamError();
            }
            return queryTradeService.queryChannelPayStatus(orderQueryVo.getTradeNo(), orderQueryVo.getChannel().getCode(), orderQueryVo.getBusinessOrderType(), orderQueryVo.getPayMode().getMode());
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#queryPayV2 warn orderCheckVo:{} paymentException:{}", JSON.toJSONString(orderQueryVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#queryPayV2 error orderCheckVo:{}", JSON.toJSONString(orderQueryVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }
    @Override
    public ResultVO<TransactionResultInfoVO> batchQueryTransactionInfo(TransactionQueryParam transactionQueryParam, String sign) {
        LOGGER.info("PaymentOrderApiImpl#batchQueryTransactionInfo transactionQueryParam:{}", JSONUtils.toJSON(transactionQueryParam));
        if(CollectionUtils.isEmpty(transactionQueryParam.getTradeNos())
                || Objects.isNull(transactionQueryParam.getTransactionType())) {
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }

        TransactionResultInfoVO vo = new TransactionResultInfoVO();
        List<TransactionResultItem> resultItems = new ArrayList<>();
        vo.setTradeList(resultItems);
        for (TransactionItem item : transactionQueryParam.getTradeNos()) {
            TransactionParam param = new TransactionParam();
            param.setTradeNo(item.getTradeNo());
            param.setChannel(item.getChannel());
            param.setPayMode(item.getPayMode());
            param.setBusinessOrderType(transactionQueryParam.getBusinessOrderType());
            param.setTransactionType(transactionQueryParam.getTransactionType());
            try {
                TransactionQuery transactionQuery = TransactionQueryFactory.getTransactionQuery(param.getTransactionType());
                TransactionResultBO transactionResultBO = transactionQuery.queryTransaction(param);

                resultItems.add(TransactionResultItem.build(transactionResultBO.getTradeNo(), transactionResultBO.getStatus()
                        , transactionResultBO.getAmount(), transactionResultBO.getChannel()
                        , transactionResultBO.getSuccessTime(), transactionResultBO.getBusinessOrderNo()));
            } catch (PaymentException e) {
                if(ResultCodeEnum.TRADE_NOT_EXIT_ERROR.getCode().equals(e.getErrCode())) {
                    // 说明无该订单
                    resultItems.add(TransactionResultItem.build(item.getTradeNo(),TransactionStatusEnum.NOT_FOUNT.getCode()));
                }else {
                    resultItems.add(TransactionResultItem.build(item.getTradeNo(), TransactionStatusEnum.TRANSACTING.getCode()));
                }
            } catch (Exception e) {
                LOGGER.error("PaymentOrderApiImpl#queryTransactionInfo error. TransactionParam:{}", JSONUtils.toJSON(param), e);
                return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR);
            }
        }

        return ResultVO.createSuccess(vo);
    }

    @Override
    public ResultVO<TransactionResultBO> queryTransactionInfo(TransactionParam param, String sign) {
        LOGGER.info("PaymentOrderApiImpl#queryTransactionInfo TransactionParam:{}", JSONUtils.toJSON(param));
        try {
            TransactionQuery transactionQuery = TransactionQueryFactory.getTransactionQuery(param.getTransactionType());
            return ResultVO.createSuccess(transactionQuery.queryTransaction(param));
        } catch (PaymentException e) {
            if(ResultCodeEnum.TRADE_NOT_EXIT_ERROR.getCode().equals(e.getErrCode())) {
                // 说明无该订单
                return ResultVO.createSuccess(TransactionResultBO.build(param.getTradeNo(), TransactionStatusEnum.NOT_FOUNT.getCode()));
            }
            if(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode().equals(e.getErrCode())) {
                // 渠道连接异常
                return ResultVO.createError(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
            if(ResultCodeEnum.DATABASE_ERROR.getCode().equals(e.getErrCode())) {
                // 本地数据库异常
                return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
            }
            return ResultVO.createError(e.getCodeEnum());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#queryTransactionInfo error. TransactionParam:{}", JSONUtils.toJSON(param), e);
            return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<List<QueryPayNoResultBo>> queryPay(QueryPayVo queryPayVo, String sign) {
        try {
            if (queryPayVo==null||queryPayVo.getInvoker()==null||
                    queryPayVo.getTradeNo()==null||queryPayVo.getTradeNo().length()>64){
                LOGGER.warn("PaymentOrderApiImpl queryPay param error queryPayVo:{}, sign:{}", JSON.toJSON(queryPayVo), sign);
                return ResultVO.createParamError();
            }
            String publicKey = ApolloUtil.getProperty(queryPayVo.getInvoker()+".public_key");
            if (!SignUtil.doCheck(queryPayVo, sign, publicKey)){
                LOGGER.error("PaymentOrderApiImpl queryPay check sign error queryPayVo:{}", JSON.toJSON(queryPayVo));
                return ResultVO.createSignError();
            }
            return queryTradeService.queryPay(queryPayVo);
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#checkPay error queryPayVo:{}", JSON.toJSONString(queryPayVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }


    @Override
    public ResultVO<AggregatePayBo> pay4cashier(OrderPayVo orderPayVo, String sign) {
        // 尝试获取令牌
        if(!rateLimiter.tryAcquire(1)) {
            LOGGER.warn("PaymentOrderApiImpl#orderPay refuse pay orderPay:{}", JSON.toJSON(orderPayVo));
            return ResultVO.create(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }
        LOGGER.info("PaymentOrderApiImpl#orderPay pay begin orderPay:{}", JSON.toJSON(orderPayVo));
        ResultVO resultVO;
        try {
            // 参数校验
            paymentService.checkOrderPay(orderPayVo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#orderPay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(orderPayVo), e.getErrCode());
            return ResultVO.createParamError();
        }
        return ResultVO.createSuccess();
    }

    @Override
    public ResultVO<AggregatePayBo> pay4cashier(OrderPayVoV1 orderPayVo, String sign) {
        // 尝试获取令牌
        if(!rateLimiter.tryAcquire(1)) {
            LOGGER.warn("PaymentOrderApiImpl#orderPay refuse pay orderPay:{}", JSON.toJSON(orderPayVo));
            return ResultVO.create(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }
        LOGGER.info("PaymentOrderApiImpl#orderPay pay begin orderPay:{}", JSON.toJSON(orderPayVo));
        ResultVO resultVO;
        try {
            // 参数校验
            paymentService.checkOrderPay(orderPayVo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#orderPay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(orderPayVo), e.getErrCode());
            return ResultVO.createParamError();
        }
        return ResultVO.createSuccess();
    }

    @Override
    public ResultVO queryRefund(RefundQueryVo refundQueryVo, String sign) {
        try {
            if(refundQueryVo.getTradeNo() == null || refundQueryVo.getInvoker() == null) {
                return ResultVO.create(ResultCodeEnum.PARAM_ERROR);
            }
            String publicKey = ApolloUtil.getProperty(refundQueryVo.getInvoker().getType() + ".public_key");
            if (!LukeRsaUtil.doCheck(refundQueryVo, sign, publicKey)){
                return ResultVO.create(ResultCodeEnum.SIGN_ERROR);
            }
            return this.queryTradeService.queryRefund(refundQueryVo.getTradeNo());
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#checkRefund warn orderCheckVo:{} paymentException:{}", JSON.toJSONString(refundQueryVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#checkRefund error orderCheckVo:{}", JSON.toJSONString(refundQueryVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<QueryResultBo> queryRefund(RefundQueryVoV1 refundQueryVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#queryRefund, refundQueryVoV1={}", JSONUtils.toJSON(refundQueryVo));
        try {
            if((refundQueryVo.getTradeNo() == null && refundQueryVo.getBusinessRefundNo() == null) || refundQueryVo.getInvoker() == null) {
                return ResultVO.create(ResultCodeEnum.PARAM_ERROR);
            }
            if (!queryTradeService.checkRequestSign(refundQueryVo.getTradeNo(), refundQueryVo.getInvoker(), sign)) {
                return ResultVO.create(ResultCodeEnum.SIGN_ERROR);
            }
            return this.queryTradeService.queryRefund(refundQueryVo.getTradeNo(), refundQueryVo.getBusinessRefundNo());
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#checkRefund warn orderCheckVo:{} paymentException:{}", JSON.toJSONString(refundQueryVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#checkRefund error orderCheckVo:{}", JSON.toJSONString(refundQueryVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public  ResultVO<PayBo> pay4noCashier(Pay4noCashierVo pay4noCashierVo, String sign){
        // 尝试获取令牌
        if(!rateLimiter.tryAcquire(1)) {
            LOGGER.warn("PaymentOrderApiImpl pay4noCashier refuse pay4noCashierVo:{}", JSON.toJSON(pay4noCashierVo));
            return ResultVO.create(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }
        LOGGER.info("PaymentOrderApiImpl#pay4noCashier begin pay4noCashierVo:{}", JSON.toJSON(pay4noCashierVo));
        ResultVO resultVO;
        try {
            // 参数校验
            paymentService.checkPay4noCashier(pay4noCashierVo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#pay4noCashier warn pay4noCashierVo:{} paymentException:{}", JSON.toJSONString(pay4noCashierVo), e.getErrCode());
            return ResultVO.createParamError();
        }

        try {
            String publicKey = ApolloUtil.getProperty(pay4noCashierVo.getInvoker().getType() + Constants.PUBLIC);
            if (!SignUtil.doCheck(pay4noCashierVo, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#pay4noCashier warn pay4noCashierVo:{} paymentException:{}", JSON.toJSONString(pay4noCashierVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            PrepayDto prepayDto = new PrepayDto();
            prepayDto.setBusinessId(pay4noCashierVo.getPayeeId());
            prepayDto.setBusinessOrderNo(pay4noCashierVo.getBusinessOrderNo());
            prepayDto.setAmount(pay4noCashierVo.getAmount());
            prepayDto.setBusinessType(pay4noCashierVo.getBusinessType().getType());
            prepayDto.setBusinessIdType(pay4noCashierVo.getBusinessIdType().getType());
            prepayDto.setOrderType(pay4noCashierVo.getBusinessType().getType());
            prepayDto.setBusinessOrderType(pay4noCashierVo.getBusinessOrderType().getType());
            prepayDto.setCallbackUrl(pay4noCashierVo.getCallback());
            prepayDto.setChannel(pay4noCashierVo.getChannel().getCode());
            prepayDto.setSubject(pay4noCashierVo.getSubject());
            prepayDto.setBody(pay4noCashierVo.getBody());
            prepayDto.setExtra(pay4noCashierVo.getMapJson());
            prepayDto.setPayerId(pay4noCashierVo.getPayerId());
            prepayDto.setPayerName(pay4noCashierVo.getPayerName());
            prepayDto.setSellerName(pay4noCashierVo.getSellerName());

            resultVO = paymentService.prepay(prepayDto);
            if (!ResultCodeEnum.SUCCESS.getCode().equals(resultVO.getCode())){
                LOGGER.error("PaymentOrderApiImpl pay4noCashier error prepayDto:{}, code:{}, msg:{}", JSON.toJSON(prepayDto), resultVO.getCode(), resultVO.getMsg());
                return resultVO;
            }
            PrepayBo prepayBo = (PrepayBo)resultVO.getResult();
            PayDto payDto = new PayDto();
            payDto.setAmount(pay4noCashierVo.getAmount());
            payDto.setBusinessOrderNo(pay4noCashierVo.getBusinessOrderNo());
            payDto.setChannel(pay4noCashierVo.getChannel().getCode());
            payDto.setBody(pay4noCashierVo.getBody());
            payDto.setPayNo(prepayBo.getPayNo());
            payDto.setPayMode(pay4noCashierVo.getPayMode().getMode());
            payDto.setBusinessOrderType(pay4noCashierVo.getBusinessOrderType().getType());
            resultVO = paymentService.pay(payDto);
            LOGGER.info("PaymentOrderApiImpl pay4noCashier end pay4noCashierVo:{}", JSON.toJSON(pay4noCashierVo));
            return resultVO;
        } catch (PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#pay4noCashier warn2 pay4noCashierVo:{} ", JSON.toJSONString(pay4noCashierVo),e);
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#pay4noCashier error orderPrepayVo:{}", JSON.toJSONString(pay4noCashierVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<PayBo> pay4noCashier(Pay4noCashierVoV1 pay4noCashierVoV1, String sign) {
        // 尝试获取令牌
        if(!rateLimiter.tryAcquire(1)) {
            LOGGER.warn("PaymentOrderApiImpl pay4noCashier refuse pay4noCashierVoV1:{}", JSON.toJSON(pay4noCashierVoV1));
            return ResultVO.create(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }

        LOGGER.info("PaymentOrderApiImpl#pay4noCashier begin pay4noCashierVoV1:{}", JSON.toJSON(pay4noCashierVoV1));
        ResultVO resultVO;

        try {
            // 参数校验
            paymentService.checkPay4noCashier(pay4noCashierVoV1);
        }
        catch(PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#pay4noCashier warn Pay4noCashierVoV1:" + JSON.toJSONString(pay4noCashierVoV1), e);
            return ResultVO.createParamError();
        }

        try {
            String publicKey = ApolloUtil.getProperty(pay4noCashierVoV1.getInvoker() + Constants.PUBLIC);

            if(!SignUtil.doCheck(pay4noCashierVoV1, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#pay4noCashier warn pay4noCashierVoV1:{} paymentException:{}", JSON.toJSONString(pay4noCashierVoV1), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            if (Constants.thirdChannelSubmitPingan(pay4noCashierVoV1.getChannel().getCode())) {
                JSONObject extra = StringUtils.isBlank(pay4noCashierVoV1.getMapJson()) ? new JSONObject() : JSONObject.parseObject(pay4noCashierVoV1.getMapJson());
                extra.put("payMode", pay4noCashierVoV1.getPayMode().getMode());
                extra.put("channel", pay4noCashierVoV1.getChannel().getCode());
                extra.put("terminalTypeEnum", null == pay4noCashierVoV1.getTerminalTypeEnum() ? "" : pay4noCashierVoV1.getTerminalTypeEnum().getChannel());
                extra.put("mobileTerminalTypeEnum", null == pay4noCashierVoV1.getMobileTerminalTypeEnum() ? "" : pay4noCashierVoV1.getMobileTerminalTypeEnum().getType());
                pay4noCashierVoV1.setMapJson(extra.toJSONString());
            }
            PrepayDto prepayDto = new PrepayDto();
            prepayDto.setBusinessId(pay4noCashierVoV1.getPayeeId());
            prepayDto.setBusinessOrderNo(pay4noCashierVoV1.getBusinessOrderNo());
            prepayDto.setAmount(pay4noCashierVoV1.getAmount());
            prepayDto.setBusinessType(pay4noCashierVoV1.getBusinessType());
            prepayDto.setBusinessIdType(pay4noCashierVoV1.getBusinessIdType());
            prepayDto.setOrderType(pay4noCashierVoV1.getBusinessType());
            prepayDto.setBusinessOrderType(pay4noCashierVoV1.getBusinessOrderType());
            prepayDto.setCallbackUrl(pay4noCashierVoV1.getCallback());
            prepayDto.setChannel(pay4noCashierVoV1.getChannel().getCode());
            prepayDto.setSubject(pay4noCashierVoV1.getSubject());
            prepayDto.setBody(pay4noCashierVoV1.getBody());
            prepayDto.setExtra(pay4noCashierVoV1.getMapJson());
            prepayDto.setPayerId(pay4noCashierVoV1.getPayerId());
            prepayDto.setPayerName(pay4noCashierVoV1.getPayerName());
            prepayDto.setSellerName(pay4noCashierVoV1.getSellerName());
            if (pay4noCashierVoV1.getMobileTerminalTypeEnum() != null) {
                prepayDto.setMobileTerminalType(pay4noCashierVoV1.getMobileTerminalTypeEnum().getType());
            }
            resultVO = paymentService.prepay(prepayDto);

            if(!ResultCodeEnum.SUCCESS.getCode().equals(resultVO.getCode())) {
                LOGGER.error("PaymentOrderApiImpl pay4noCashier error prepayDto:{}, code:{}, msg:{}",
                        JSON.toJSON(prepayDto), resultVO.getCode(), resultVO.getMsg());
                return resultVO;
            }

            PrepayBo prepayBo = (PrepayBo)resultVO.getResult();
            PayDto payDto = new PayDto();
            payDto.setAmount(pay4noCashierVoV1.getAmount());
            payDto.setBusinessOrderNo(pay4noCashierVoV1.getBusinessOrderNo());
            payDto.setChannel(pay4noCashierVoV1.getChannel().getCode());
            payDto.setBody(pay4noCashierVoV1.getBody());
            payDto.setPayNo(prepayBo.getPayNo());
            payDto.setPayMode(pay4noCashierVoV1.getPayMode().getMode());
            payDto.setTimeExpire(prepayDto.getTimeExpire());
            payDto.setSubject(prepayDto.getSubject());
            payDto.setCreateTime(prepayBo.getCreateTime());
            payDto.setBusinessOrderType(prepayDto.getBusinessOrderType());
            payDto.setPayerId(pay4noCashierVoV1.getPayerId());
            payDto.setBusinessId(pay4noCashierVoV1.getPayeeId());

            if(pay4noCashierVoV1.getPayResultMode() != null) {
                payDto.setPayResultMode(pay4noCashierVoV1.getPayResultMode().getMode());
            }

            if(!StringUtil.isEmpty(pay4noCashierVoV1.getMapJson())) {
                Map<String, Object> mapJson = JSONUtils.parse2Map(pay4noCashierVoV1.getMapJson(), String.class, Object.class);
                payDto.setMap(mapJson);
                if (CollectionUtil.isNotEmpty(mapJson)) {
                    payDto.setReceiveName((String) mapJson.get(PayExtraConstants.PAY_RECEIVEACTNAME_EXTRA_NAME));
                    payDto.setReceiveAccount((String) mapJson.get(PayExtraConstants.PAY_RECEIVEACT_EXTRA_NAME));
                    payDto.setMerchantUscc((String) mapJson.get(PayExtraConstants.PAY_MERCHANTUSCC_EXTRA_NAME));
                }
            }

            if (pay4noCashierVoV1.getMobileTerminalTypeEnum() != null) {
                payDto.setMobileTerminalType(pay4noCashierVoV1.getMobileTerminalTypeEnum().getType());
            }
//            payDto.setTerminalTypeEnum()
            resultVO = paymentService.channelDegradedPay(payDto);
            LOGGER.info("PaymentOrderApiImpl pay4noCashier end pay4noCashierVo:{} result:{}", JSON.toJSON(pay4noCashierVoV1), JSON.toJSON(resultVO));
            return resultVO;
        }
        catch(PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#pay4noCashier error pay4noCashierVoV1:{}", JSON.toJSONString(pay4noCashierVoV1), e);
            return ResultVO.createError(e.getErrCode(), e.getErrMsg());
        }
        catch(Exception e) {
            LOGGER.error("PaymentOrderApiImpl#pay4noCashier error orderPrepayVo:{}", JSON.toJSONString(pay4noCashierVoV1), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO pay(PayVo payVo, String sign) {
        // 尝试获取令牌
        if(!rateLimiter.tryAcquire(1)) {
            LOGGER.warn("PaymentOrderApiImpl#pay refuse pay payVo:{}", JSON.toJSON(payVo));
            return ResultVO.create(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }
        LOGGER.info("PaymentOrderApiImpl#pay pay begin payVo:{}", JSON.toJSON(payVo));
        ResultVO resultVO;
        try {
            // 参数校验
            paymentService.checkPayVo(payVo);
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#pay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(payVo), e.getErrCode());
            return ResultVO.createParamError();
        }

        Map<String, Object> map = new HashMap<>(16);
        map.put("payeeId", payVo.getPayeeId());
        map.put("businessOrderNo", payVo.getBusinessOrderNo());
        map.put("amount", payVo.getAmount());
        map.put("invoker", payVo.getInvoker());
        map.put("businessType", payVo.getBusinessType());
        map.put("businessIdType", payVo.getBusinessIdType());
        map.put("businessOrderType", payVo.getBusinessOrderType());
        map.put("callback", payVo.getCallback());
        map.put("channel", payVo.getChannel());
        map.put("body", payVo.getBody());
        map.put("mapJson", payVo.getMapJson());
        map.put("payMode", payVo.getPayMode());
        try {
            String publicKey = ApolloUtil.getProperty(payVo.getInvoker().getType() + Constants.PUBLIC);
            if (!SignUtil.doCheck(map, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#pay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(payVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            PrepayDto prepayDto = new PrepayDto();
            prepayDto.setBusinessId(payVo.getPayeeId());
            prepayDto.setBusinessOrderNo(payVo.getBusinessOrderNo());
            prepayDto.setAmount(payVo.getAmount());
            prepayDto.setBusinessType(payVo.getBusinessType().getType());
            prepayDto.setBusinessIdType(payVo.getBusinessIdType().getType());
            prepayDto.setBusinessOrderType(payVo.getBusinessOrderType().getType());
            prepayDto.setCallbackUrl(payVo.getCallback());
            prepayDto.setChannel(payVo.getChannel().getCode());
            resultVO = paymentService.prepay(prepayDto);
            if (ResultCodeEnum.SUCCESS.getCode()!=resultVO.getCode()){
                LOGGER.error("PaymentOrderApiImpl pay pay error prepayDto:{}, code:{}, msg:{}", JSON.toJSON(prepayDto), resultVO.getCode(), resultVO.getMsg());
                return resultVO;
            }
            PrepayBo prepayBo = (PrepayBo) resultVO.getResult();
            if (PayModeEnum.APP.getMode().equals(payVo.getPayMode().getMode())){
                LOGGER.info("PaymentOrderApiImpl pay appPay begin businessOrderNo:{}", payVo.getBusinessOrderNo());
                AppPayDto appPayDto = new AppPayDto();
                appPayDto.setBusinessOrderNo(payVo.getBusinessOrderNo());
                appPayDto.setChannel(payVo.getChannel().getCode());
                appPayDto.setBody(payVo.getBody());
                appPayDto.setPayNo(prepayBo.getPayNo());
                if (!StringUtil.isEmpty(payVo.getMapJson())){
                    Map<String, Object> mapJson = JSONUtils.parse2Map(payVo.getMapJson(), String.class, Object.class);
                    appPayDto.setMap(mapJson);
                }
                resultVO = paymentService.appPay(appPayDto);
                LOGGER.info("PaymentOrderApiImpl pay end payVo:{}", JSON.toJSON(payVo));
                return resultVO;
            }
            if (PayModeEnum.MINI.getMode().equals(payVo.getPayMode().getMode())){
                LOGGER.info("PaymentOrderApiImpl pay MiniPay begin businessOrderNo:{}", payVo.getBusinessOrderNo());
                MiniPayDto miniPayDto = new MiniPayDto();
                miniPayDto.setBusinessOrderNo(payVo.getBusinessOrderNo());
                miniPayDto.setChannel(payVo.getChannel().getCode());
                miniPayDto.setBody(payVo.getBody());
                miniPayDto.setPayNo(prepayBo.getPayNo());
                if (!StringUtil.isEmpty(payVo.getMapJson())){
                    Map<String, Object> mapJson = JSONUtils.parse2Map(payVo.getMapJson(), String.class, Object.class);
                    miniPayDto.setMap(mapJson);
                }
                resultVO = paymentService.miniPay(miniPayDto);
                LOGGER.info("PaymentOrderApiImpl pay end payVo:{}", JSON.toJSON(payVo));
                return resultVO;
            }
        } catch (PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#pay warn orderPrepayVo:{} paymentException:{}", JSON.toJSONString(payVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#pay error orderPrepayVo:{}", JSON.toJSONString(payVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
        LOGGER.info("PaymentOrderApiImpl pay end payVo:{}", JSON.toJSON(payVo));
        return ResultVO.createSuccess();
    }

    @Override
    public ResultVO withdraw(OrderWithdrawVo orderWithdrawVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#withdraw begin orderWithdrawVo:{}", JSON.toJSONString(orderWithdrawVo));

        try {
            paymentService.checkOrderWithdrawVo(orderWithdrawVo);
        }
        catch(PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#withdraw warn orderWithdrawVo:{} paymentException:{}", JSON.toJSONString(orderWithdrawVo), ResultCodeEnum.SIGN_ERROR.getCode());
            return ResultVO.createError(e.getErrCode(), e.getErrMsg());
        }

        try {
            String publicKey = ApolloUtil.getProperty(orderWithdrawVo.getInvoker() + ".public_key");

            if(!SignUtil.doCheck(orderWithdrawVo, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#withdraw warn orderWithdrawVo:{} paymentException:{}", JSON.toJSONString(orderWithdrawVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }

            WithdrawalDto withdrawalDto = new WithdrawalDto();
            withdrawalDto.setAmount(orderWithdrawVo.getAmount());
            withdrawalDto.setBusinessId(orderWithdrawVo.getBusinessId());
            withdrawalDto.setDescription(orderWithdrawVo.getDescription());
            withdrawalDto.setCallbackUrl(orderWithdrawVo.getCallback());
            withdrawalDto.setBusinessType(orderWithdrawVo.getBusinessType());
            withdrawalDto.setAccountChannel(orderWithdrawVo.getAccountChannelEnum().getChannel());
            withdrawalDto.setBusinessIdType(orderWithdrawVo.getBusinessIdType());
            withdrawalDto.setBusinessWithdrawNo(orderWithdrawVo.getBusinessWithdrawNo());

            OrderWithdrawalBo orderWithdrawalBo = paymentService.withdraw(withdrawalDto);
            LOGGER.info("PaymentOrderApiImpl#withdraw end orderWithdrawalBo:{}", JSON.toJSONString(orderWithdrawalBo));
            return ResultVO.createSuccess(orderWithdrawalBo);
        }
        catch(PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#withdraw warn orderWithdrawalBo=" + JSON.toJSONString(orderWithdrawVo), e);
            return ResultVO.createError(e.getErrCode(), e.getErrMsg());
        }
        catch(Exception e) {
            LOGGER.error("PaymentOrderApiImpl#withdraw error orderWithdrawalBo=" + JSON.toJSONString(orderWithdrawVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO withdrawAuth(WithdrawAuthVo withdrawAuthVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#withdrawAuth begin withdrawAuthVo:{}", JSON.toJSONString(withdrawAuthVo));

        // 1.参数校验
        if(StringUtils.isBlank(withdrawAuthVo.getBusinessIdType())) {
            return ResultVO.createError(ResultCodeEnum.BUSINESS_ID_TYPE_IS_NULL);
        }
        else if(StringUtils.isBlank(withdrawAuthVo.getWithdrawalNo()) && StringUtils.isBlank(withdrawAuthVo.getBusinessWithdrawNo())) {
            return ResultVO.createError(ResultCodeEnum.TRADE_ORDER_IS_NULL);
        }
        else if(StringUtils.isBlank(withdrawAuthVo.getActiveCode())) {
            return ResultVO.createError(ResultCodeEnum.ACTIVE_CODE_IS_NULL_ERROR);
        }

        try {
            OrderWithdrawalBo orderWithdrawalBo = paymentService.withdrawalAuth(withdrawAuthVo);
            LOGGER.info("PaymentOrderApiImpl#withdrawAuth end orderWithdrawalBo:{}", JSON.toJSONString(orderWithdrawalBo));
            return ResultVO.createSuccess(orderWithdrawalBo);
        }
        catch(Exception ex) {
            LOGGER.error("PaymentOrderApiImpl#withdrawAuth error. message=" + JSON.toJSONString(withdrawAuthVo), ex);

            if(ex instanceof PaymentException) {
                return ResultVO.createError(((PaymentException) ex).getErrCode(), ((PaymentException) ex).getErrMsg());
            }
            else {
                return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
            }
        }
    }

    @Override
    public ResultVO<QueryWithdrawalFeeBo> queryWithdrawalFee(QueryWithdrawalFeeVo queryWithdrawalFeeVo, String sign) {
        String publicKey = ApolloUtil.getProperty(queryWithdrawalFeeVo.getInvoker() + ".public_key");
        if(queryWithdrawalFeeVo.getAmount() == null || queryWithdrawalFeeVo.getAmount() < 0 || queryWithdrawalFeeVo.getBusinessIdType() == null
                || queryWithdrawalFeeVo.getBusinessUserId() == null || queryWithdrawalFeeVo.getInvoker() == null) {
           return ResultVO.createParamError();
        }
        try {
            if (!SignUtil.doCheck(queryWithdrawalFeeVo, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#withdraw warn orderWithdrawVo:{} paymentException:{}", JSON.toJSONString(queryWithdrawalFeeVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            QueryWithdrawalFeeBo queryWithdrawalFeeBo = paymentService.withdrawalFee(queryWithdrawalFeeVo);
            return ResultVO.createSuccess(queryWithdrawalFeeBo);
        } catch (PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#queryWithdrawalFee warn queryWithdrawalFeeVo:{} paymentException:{}", JSON.toJSONString(queryWithdrawalFeeVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#queryWithdrawalFee error queryWithdrawalFeeVo:{}", JSON.toJSONString(queryWithdrawalFeeVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    /**
     * 订单关闭接口
     *
     * @param orderClosedVo
     * @param sign
     * @return
     */
    @Override
    public ResultVO<OrderClosedBo> orderClosed(OrderClosedVo orderClosedVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#orderClosed begin orderClosedVo:{}", JSON.toJSONString(orderClosedVo));
        OrderClosedBo orderClosedBo = new OrderClosedBo();
        try {
            orderClosedBo.setPayNo(orderClosedVo.getPayNo());
            paymentService.checkOrderClosedVo(orderClosedVo);
            String publicKey = ApolloUtil.getProperty(orderClosedVo.getInvoker() + ".public_key");
            if (!SignUtil.doCheck(orderClosedVo, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#orderClosed warn orderClosedVo:{} paymentException:{}", JSON.toJSONString(orderClosedVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
            OrderClosedDto orderClosedDto = new OrderClosedDto();
            orderClosedDto.setChannel(orderClosedVo.getChannel().getCode());
            orderClosedDto.setBusinessOrderType(orderClosedVo.getBusinessOrderType());
            orderClosedDto.setBusinessType(orderClosedVo.getBusinessType());
            orderClosedDto.setPayNo(orderClosedVo.getPayNo());
            orderClosedBo = paymentService.orderClosed(orderClosedDto);
        }catch (PaymentException e){
            orderClosedBo.setStatus((byte)0);
            LOGGER.info("PaymentOrderApiImpl#orderClosed end orderClosedVo:{}", JSON.toJSONString(orderClosedVo));
            return ResultVO.createError(e.getErrCode(),e.getErrMsg());
        }catch (Exception e) {
            orderClosedBo.setStatus((byte)0);
            LOGGER.info("PaymentOrderApiImpl#orderClosed end orderClosedVo:{}", JSON.toJSONString(orderClosedVo), e);
            return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR);
        }
        return ResultVO.createSuccess(orderClosedBo);
    }

    @Override
    public ResultVO<SeparateAccountsBo> separateAccounts(SeparateAccountsVo separateAccountsVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#separateAccounts begin separateAccountsVo:{}", JSON.toJSONString(separateAccountsVo));
        return ResultVO.createError(ResultCodeEnum.UN_SUPPORT_METHOD);
    }
    @Override
    public void clearFrozenAmount(List<String> list) {
        for (String fBankAccountId : list) {
            // 4.异步处理富民冻结金额
            callbackRunner.syncSendMsg(RocketMqTopic.TOPIC_FM_FROZEN_AMOUNT_HANDLE, fBankAccountId + PayAccount.SUFFIX_DELETE);

        }
    }
    @Override
    public ResultVO<SeparateAccountsBo> separateAccountFromUser(SeparateAccountFromUserVo separateAccountFromUserVo, String sign) {
        LOGGER.info("PaymentOrderApiImpl#separateAccountFromUser begin separateAccountFromUserVo:{}", JSON.toJSONString(separateAccountFromUserVo));
        boolean paramError = separateAccountFromUserVo == null || separateAccountFromUserVo.getBusinessOrderNo() == null || separateAccountFromUserVo.getBusinessOrderType() == null;

        if(paramError) {
            LOGGER.warn("PaymentOrderApiImpl#separateAccountFromUser param error separateAccountFromUserVo:{}", JSON.toJSONString(separateAccountFromUserVo));
            return ResultVO.createParamError();
        }

        if(separateAccountFromUserVo.getAmount() <= 0) {
            LOGGER.warn("PaymentOrderApiImpl#separateAccountFromUser amountError error separateAccountFromUserVo:{}", JSON.toJSONString(separateAccountFromUserVo));
            return ResultVO.createParamError("金额需要大于0");
        }

        //防止连点 value随便设置
        if(!StringUtil.isEmpty(redisClient.get("separateAccountFromUser" + separateAccountFromUserVo.getBusinessOrderNo()))) {
            return ResultVO.create(ResultCodeEnum.NOT_CONTINUOUS_CLICK);
        }

        redisClient.set("separateAccountFromUser" + separateAccountFromUserVo.getBusinessOrderNo(), "1", 5L);
        SeparateAccountsBo separateAccountsBo = null;

        try {
            String publicKey = ApolloUtil.getProperty(separateAccountFromUserVo.getInvoker() + ".public_key");

            if(!SignUtil.doCheck(separateAccountFromUserVo, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#separateAccountFromUser warn separateAccountFromUserVo:{} paymentException:{}",
                        JSON.toJSONString(separateAccountFromUserVo), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }

            separateAccountsBo = paymentService.separateAccountsFromUser(separateAccountFromUserVo);
        }
        catch(PaymentException e){
            LOGGER.error("PaymentOrderApiImpl#separateAccountFromUser end separateAccountFromUserVo=" + JSON.toJSONString(separateAccountFromUserVo), e);
            return ResultVO.createError(e.getErrCode(), e.getErrMsg());
        }
        catch(Exception e) {
            LOGGER.info("PaymentOrderApiImpl#separateAccountFromUser end separateAccountFromUserVo=" + JSON.toJSONString(separateAccountFromUserVo), e);
            return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR);
        }

        return ResultVO.createSuccess(separateAccountsBo);
    }

    @Override
    public ResultVO<BatchSeparateAccountsBo> batchSeparateAccount(BatchSeparateAccountVO batchSeparateAccountVO, String sign) {
        LOGGER.info("PaymentOrderApiImpl#batchSeparateAccount begin batchSeparateAccountVO:{}", JSON.toJSONString(batchSeparateAccountVO));

        // 参数校验
        ResultVO checkParamResult = checkBatchSeparateAccountParam(batchSeparateAccountVO);

        if(!ResultCodeEnum.SUCCESS.getCode().equals(checkParamResult.getCode())) {
            LOGGER.info("PaymentOrderApiImpl#batchSeparateAccount param error. checkParamResult:{}", JSON.toJSONString(checkParamResult));
            return checkParamResult;
        }

        //防止连点 value随便设置
        if(!StringUtil.isEmpty(redisClient.get("batchSeparateAccount_"+batchSeparateAccountVO.getBusinessOrderNo()))) {
            return ResultVO.create(ResultCodeEnum.NOT_CONTINUOUS_CLICK);
        }

        redisClient.set("batchSeparateAccount_"+batchSeparateAccountVO.getBusinessOrderNo(), "1", 5L);

        // 验签
        try {
            String publicKey = ApolloUtil.getProperty(batchSeparateAccountVO.getInvoker() + ".public_key");

            if(!SignUtil.doCheck(batchSeparateAccountVO, sign, publicKey)) {
                LOGGER.warn("PaymentOrderApiImpl#batchSeparateAccount warn batchSeparateAccountVO:{} paymentException:{}", JSON.toJSONString(batchSeparateAccountVO), ResultCodeEnum.SIGN_ERROR.getCode());
                return ResultVO.createSignError();
            }
        }
        catch(Exception e) {
            LOGGER.info("PaymentOrderApiImpl#batchSeparateAccount end batchSeparateAccountVO:{}", JSON.toJSONString(batchSeparateAccountVO), e);
            return ResultVO.createError(ResultCodeEnum.DEFAULT_ERROR);
        }

        SeparateSummary record = separateSummaryService.selectByBusinessOrderNo(batchSeparateAccountVO.getBusinessOrderNo());

        if(null != record && record.getStatus() != SeparateSummaryStatusEnum.SEPARATE_FAIL.getStatus()) {
            if(SeparateSummaryStatusEnum.IN_SEPARATE.getStatus() == record.getStatus()) {
                return ResultVO.create(ResultCodeEnum.SEPARATE_ORDER_SUCCESS_REPEAT_REQUEST);
            }

            if(SeparateSummaryStatusEnum.SEPARATE_SUCCESS.getStatus() == record.getStatus()) {
                return ResultVO.create(ResultCodeEnum.SEPARATE_ORDER_REPEAT_REQUEST_IN_SEPARATE);
            }

            return ResultVO.createError(SeparateSummaryStatusEnum.getDescByStatus(record.getStatus()));
        }

        String separateNo = record == null ? leafUtil.getSnowflakeId() : record.getSeparateNo();

        if(null != record) {
            // 更新主表
            SeparateSummary separateSummary = new SeparateSummary();
            separateSummary.setId(record.getId());
            separateSummary.setBusinessType(batchSeparateAccountVO.getBusinessType());
            separateSummary.setBusinessIdType(batchSeparateAccountVO.getBusinessIdType());
            separateSummary.setBusinessOrderType(batchSeparateAccountVO.getBusinessOrderType());
            separateSummary.setBusinessUserId(batchSeparateAccountVO.getBusinessUserId());
            separateSummary.setCallback(batchSeparateAccountVO.getCallback());
            separateSummary.setErrMsg("");
            separateSummary.setSeparateDetails(JSONUtils.toJSON(batchSeparateAccountVO.getDetails()));
            separateSummary.setStatus(SeparateSummaryStatusEnum.CREATE.getStatus());
            separateSummary.setUpdateTime(DateUtil.now());
            int rows = separateSummaryService.updateById(separateSummary);

            if(rows < 1) {
                LOGGER.error("PaymentOrderApiImpl#batchSeparateAccount updateById error separateSummary:{}", JSON.toJSONString(separateSummary));
                return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
            }
        }
        else {
            // 存转账主表
            SeparateSummary separateSummary = new SeparateSummary();
            separateSummary.setSeparateNo(separateNo);
            separateSummary.setBusinessType(batchSeparateAccountVO.getBusinessType());
            separateSummary.setBusinessIdType(batchSeparateAccountVO.getBusinessIdType());
            separateSummary.setBusinessOrderType(batchSeparateAccountVO.getBusinessOrderType());
            separateSummary.setBusinessOrderNo(batchSeparateAccountVO.getBusinessOrderNo());
            separateSummary.setBusinessUserId(batchSeparateAccountVO.getBusinessUserId());
            separateSummary.setCallback(batchSeparateAccountVO.getCallback());
            separateSummary.setErrMsg("");
            separateSummary.setSeparateDetails(JSONUtils.toJSON(batchSeparateAccountVO.getDetails()));
            separateSummary.setStatus(SeparateSummaryStatusEnum.CREATE.getStatus());
            Date now = DateUtil.now();
            separateSummary.setCreateTime(now);
            separateSummary.setUpdateTime(now);
            int rows = separateSummaryService.insert(separateSummary);

            if(rows < 1) {
                LOGGER.error("PaymentOrderApiImpl#batchSeparateAccount insert error separateSummary:{}", JSON.toJSONString(separateSummary));
                return ResultVO.createError(ResultCodeEnum.DATABASE_ERROR);
            }
        }

        // 发送mq
        mqProducer.syncSendMsg(RocketMqTopic.TOPIC_SEPARATE_ORDER_CONSUMER, separateNo);

        BatchSeparateAccountsBo result = new BatchSeparateAccountsBo();
        result.setTradeNo(separateNo);
        LOGGER.info("PaymentOrderApiImpl#batchSeparateAccount end BatchSeparateAccountsBo:{}", JSON.toJSONString(result));
        return ResultVO.createSuccess(result);
    }

    private ResultVO checkBatchSeparateAccountParam(BatchSeparateAccountVO batchSeparateAccountVO) {
        if(null == batchSeparateAccountVO
                || StringUtils.isBlank(batchSeparateAccountVO.getBusinessOrderNo())
                || StringUtils.isBlank(batchSeparateAccountVO.getBusinessIdType())
                || StringUtils.isBlank(batchSeparateAccountVO.getBusinessOrderType())
                || StringUtils.isBlank(batchSeparateAccountVO.getBusinessType())
                || StringUtils.isBlank(batchSeparateAccountVO.getCallback())) {
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
        if(CollectionUtils.isEmpty(batchSeparateAccountVO.getDetails())) {
            return ResultVO.createError("转账明细为空!");
        }
        if (batchSeparateAccountVO.getBusinessOrderNo().length() > Constants.MAX_STRING_LENGTH_NO ) {
            return ResultVO.createError(ResultCodeEnum.TRADE_NO_VERY_LONG_ERROR);
        }
        if(!businessTypeConfig.checkAllType(batchSeparateAccountVO.getBusinessType(), batchSeparateAccountVO.getBusinessIdType(),batchSeparateAccountVO.getBusinessOrderType())) {
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
        return ResultVO.createSuccess();
    }

    /**
     * 根据单号查询支付渠道
     *
     * @param queryPayChannelParam
     * @param sign
     * @return
     */
    @Override
    public ResultVO<Map<String, String>> queryPayChannelByOrderNo(QueryPayChannelParam queryPayChannelParam, String sign) {
        if(null == queryPayChannelParam || null == queryPayChannelParam.getOrderType()
            || null == queryPayChannelParam.getBusinessOrderType() || null == queryPayChannelParam.getBusinessType()
            || null == queryPayChannelParam.getInvokerEnum() || CollectionUtils.isEmpty(queryPayChannelParam.getPaymentOrderNo())) {
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
        Map<String, String> result = new HashMap<>();
        List<String> payNoList = queryPayChannelParam.getPaymentOrderNo();
        switch (queryPayChannelParam.getOrderType()) {
            case PAY:
                for (String payNo : payNoList) {
                    List<PayResultOrder> payResultOrders = payResultOrderService.selectListByPayNo(payNo);
                    for (PayResultOrder payResultOrder : payResultOrders) {
                        if(queryPayChannelParam.getBusinessOrderType().getType().equals(payResultOrder.getBusinessOrderType())
                            && queryPayChannelParam.getBusinessType().getType().equals(payResultOrder.getBusinessType())) {
                            result.put(payNo,payResultOrder.getPayChannel());
                        }
                    }
                }
                return ResultVO.createSuccess(result);
            case REFUND:
                for (String refundNo : payNoList) {
                    RefundOrder refundOrder = refundOrderService.selectByRefundNo(refundNo);
                    // TODO 退款单无 businessOrderType
                    if(null != refundOrder && queryPayChannelParam.getBusinessType().getType().equals(refundOrder.getBusinessType())) {
                        result.put(refundNo,refundOrder.getChannel());
                    }
                }
                return ResultVO.createSuccess(result);
            default:
                return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
    }

    @Override
    public ResultVO<OrderWithdrawalBo> queryWithdrawNoByBusinessOrderNo(OrderWithdrawalQueryParam queryParam, String sign) {
        if(null == queryParam || null == queryParam.getBusinessType() || StringUtils.isBlank(queryParam.getBusinessWithdrawNo())) {
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }
        WithdrawOrder withdrawOrder = withdrawOrderMapper.selectByBusinessWithdrawNo(queryParam.getBusinessType().getType(), queryParam.getBusinessWithdrawNo());
        OrderWithdrawalBo result = new OrderWithdrawalBo();
        if(null != withdrawOrder) {
            result.setWithdrawalNo(withdrawOrder.getWithdrawNo());
        }
        return ResultVO.createSuccess(result);
    }


    @Override
    public ResultVO queryWithdraw(OrderQueryVoV1 orderQueryVo, String sign) {
        try {
            String publicKey = ApolloUtil.getProperty(orderQueryVo.getInvoker() + ".public_key");
            if (!SignUtil.doCheck(orderQueryVo, sign, publicKey)) {
                return ResultVO.create(ResultCodeEnum.SIGN_ERROR);
            }
            return this.queryTradeService.queryWithdraw(orderQueryVo.getTradeNo());
        } catch (PaymentException e) {
            LOGGER.warn("PaymentOrderApiImpl#checkWithdraw warn orderCheckVo:{} paymentException:{}", JSON.toJSONString(orderQueryVo), e.getErrCode());
            return ResultVO.create(e.getErrCode());
        } catch (Exception e) {
            LOGGER.error("PaymentOrderApiImpl#checkWithdraw error orderCheckVo:{}", JSON.toJSONString(orderQueryVo), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public ResultVO<TradeCertificateBo> queryTradeCertificate(QueryTradeCertificateVo queryParam, String sign) {
        LOGGER.info("PaymentOrderApiImpl#queryTradeCertificate, queryParam={}", JSONUtils.toJSON(queryParam));

        if (StringUtils.isBlank(queryParam.getTradeNo()) ||
                queryParam.getTradeType() == null ||
                StringUtils.isBlank(queryParam.getPayChannel())) {
            LOGGER.error("PaymentOrderApiImpl#queryTradeCertificate param error, queryParam={}", JSONUtils.toJSON(queryParam));
            return ResultVO.createError(ResultCodeEnum.PARAM_ERROR);
        }

        try {
            TradeCertificateVo tradeCertificateVo = separateOrderService2.queryTradeCertificate(QueryTradeCertificateDto.builder()
                    .payChannel(queryParam.getPayChannel())
                    .tradeNo(queryParam.getTradeNo())
                    .tradeType(queryParam.getTradeType().getCode()).build());
            TradeCertificateBo tradeCertificateBo = new TradeCertificateBo();
            tradeCertificateBo.setTradeNo(queryParam.getTradeNo());
            tradeCertificateBo.setTradeType(queryParam.getTradeType());
            tradeCertificateBo.setChannelTradeNo(tradeCertificateVo.getChannelTradeNo());
            tradeCertificateBo.setCheckCode(tradeCertificateVo.getCheckCode());
            return ResultVO.createSuccess(tradeCertificateBo);
        } catch (PaymentException e) {
            LOGGER.error("PaymentOrderApiImpl#queryTradeCertificate error queryParam:{}", JSON.toJSONString(queryParam), e);
            return ResultVO.create(ResultCodeEnum.DEFAULT_ERROR);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Set set = new HashSet();
        set.add(Constants.PAY_RATE_LIMITER_CREATE);
        set.add(Constants.PINGXX_BUSINESS_ORDER_CONFIG);
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                rateLimiter = RateLimiter.create(Integer.parseInt(ApolloUtil.getProperty(Constants.PAY_RATE_LIMITER_CREATE)));
            }
        }, set);
    }
}
